import ctypes
import datetime
from enum import IntEnum

# 加载lib60870库（请根据实际库路径调整）
# lib = ctypes.CDLL("lib60870.so")  # Linux
# lib = ctypes.CDLL("lib60870.dll")  # Windows
lib60870 = ctypes.CDLL("./lib60870-C//b1/tools/bin/debug/lib60870.dll")  # 假设库文件名为lib60870.so


# 枚举类型定义
class CS101_CauseOfTransmission(IntEnum):
    PERIODIC = 1
    BACKGROUND_SCAN = 2
    SPONTANEOUS = 3
    INITIALIZED = 4
    REQUEST = 5
    ACTIVATION = 6
    ACTIVATION_CON = 7
    DEACTIVATION = 8
    DEACTIVATION_CON = 9
    ACTIVATION_TERM = 10
    RETURN_INFO_REMOTE = 11
    RETURN_INFO_LOCAL = 12
    FILE_TRANSFER = 13


class QualifierOfInterrogation(IntEnum):
    STATION_INTERROGATION = 20
    GROUP_1 = 1
    GROUP_2 = 2
    GROUP_3 = 3
    GROUP_4 = 4
    GROUP_5 = 5
    GROUP_6 = 6
    GROUP_7 = 7
    GROUP_8 = 8
    GROUP_9 = 9
    GROUP_10 = 10
    GROUP_11 = 11
    GROUP_12 = 12
    GROUP_13 = 13
    GROUP_14 = 14
    GROUP_15 = 15
    CLEAR = 0


class CS104_ConnectionEvent(IntEnum):
    CONNECTION_OPENED = 0
    CONNECTION_CLOSED = 1
    STARTDT_CON_RECEIVED = 2
    STOPDT_CON_RECEIVED = 3
    CONNECTION_FAILED = 4


# 时间结构体定义 (CP56Time2a)
class CP56Time2a(ctypes.Structure):
    _fields_ = [
        ("milliseconds", ctypes.c_uint16),
        ("minutes", ctypes.c_uint8),
        ("hours", ctypes.c_uint8),
        ("day", ctypes.c_uint8),
        ("month", ctypes.c_uint8),
        ("year", ctypes.c_uint8),
        ("is_valid", ctypes.c_uint8, 1),
        ("summer_time", ctypes.c_uint8, 2)
    ]

    @classmethod
    def from_datetime(cls, dt: datetime.datetime):
        """从datetime对象创建CP56Time2a"""
        obj = cls()
        obj.milliseconds = dt.microsecond // 1000
        obj.minutes = dt.minute
        obj.hours = dt.hour
        obj.day = dt.day
        obj.month = dt.month
        obj.year = dt.year % 100  # 只取后两位
        obj.is_valid = 1
        obj.summer_time = 0
        return obj


# 回调函数类型定义
ASDUReceivedHandler = ctypes.CFUNCTYPE(
    None,
    ctypes.c_void_p,  # parameter
    ctypes.c_void_p   # CS101_ASDU
)

ConnectionEventHandler = ctypes.CFUNCTYPE(
    None,
    ctypes.c_void_p,  # parameter
    ctypes.c_int      # CS104_ConnectionEvent
)


class CS104Client:
    def __init__(self, hostname: str, tcp_port: int = -1):
        self.hostname = hostname.encode('utf-8')
        self.tcp_port = tcp_port
        self.connection = None
        self.asdu_handler = None
        self.connection_handler = None

        # 初始化连接
        self._init_library_functions()

    def _init_library_functions(self):
        """初始化库函数原型"""
        # 创建连接
        lib60870.CS104_Connection_create.argtypes = [ctypes.c_char_p, ctypes.c_int]
        lib60870.CS104_Connection_create.restype = ctypes.c_void_p

        # 安全连接（TLS）
        # lib60870.CS104_Connection_createSecure.argtypes = [ctypes.c_char_p, ctypes.c_int, ctypes.c_void_p]
        # lib60870.CS104_Connection_createSecure.restype = ctypes.c_void_p

        # 连接
        lib60870.CS104_Connection_connect.argtypes = [ctypes.c_void_p]
        lib60870.CS104_Connection_connect.restype = ctypes.c_bool

        # 异步连接
        lib60870.CS104_Connection_connectAsync.argtypes = [ctypes.c_void_p]
        lib60870.CS104_Connection_connectAsync.restype = None

        # 发送StartDT
        lib60870.CS104_Connection_sendStartDT.argtypes = [ctypes.c_void_p]
        lib60870.CS104_Connection_sendStartDT.restype = None

        # 发送StopDT
        lib60870.CS104_Connection_sendStopDT.argtypes = [ctypes.c_void_p]
        lib60870.CS104_Connection_sendStopDT.restype = None

        # 设置ASDU接收回调
        lib60870.CS104_Connection_setASDUReceivedHandler.argtypes = [
            ctypes.c_void_p, ASDUReceivedHandler, ctypes.c_void_p
        ]
        lib60870.CS104_Connection_setASDUReceivedHandler.restype = None

        # 发送 interrogation 命令
        lib60870.CS104_Connection_sendInterrogationCommand.argtypes = [
            ctypes.c_void_p, ctypes.c_int, ctypes.c_int, ctypes.c_int
        ]
        lib60870.CS104_Connection_sendInterrogationCommand.restype = ctypes.c_bool

        # 发送时钟同步命令
        lib60870.CS104_Connection_sendClockSyncCommand.argtypes = [
            ctypes.c_void_p, ctypes.c_int, CP56Time2a
        ]
        lib60870.CS104_Connection_sendClockSyncCommand.restype = ctypes.c_bool

        # 发送读命令
        lib60870.CS104_Connection_sendReadCommand.argtypes = [
            ctypes.c_void_p, ctypes.c_int, ctypes.c_int
        ]
        lib60870.CS104_Connection_sendReadCommand.restype = ctypes.c_bool

        # 检查发送缓冲区是否满
        lib60870.CS104_Connection_isTransmitBufferFull.argtypes = [ctypes.c_void_p]
        lib60870.CS104_Connection_isTransmitBufferFull.restype = ctypes.c_bool

    def connect(self, blocking: bool = True) -> bool:
        """创建并连接到服务器"""
        self.connection = lib60870.CS104_Connection_create(self.hostname, self.tcp_port)
        if not self.connection:
            return False

        if blocking:
            return lib60870.CS104_Connection_connect(self.connection)
        else:
            lib60870.CS104_Connection_connectAsync(self.connection)
            return True

    def connect_secure(self, tls_config, blocking: bool = True) -> bool:
        """创建并连接到服务器（TLS加密）"""
        self.connection = lib60870.CS104_Connection_createSecure(
            self.hostname, self.tcp_port, tls_config
        )
        if not self.connection:
            return False

        if blocking:
            return lib60870.CS104_Connection_connect(self.connection)
        else:
            lib60870.CS104_Connection_connectAsync(self.connection)
            return True

    def start_data_transmission(self):
        """开始数据传输"""
        if self.connection:
            lib60870.CS104_Connection_sendStartDT(self.connection)

    def stop_data_transmission(self):
        """停止数据传输"""
        if self.connection:
            lib60870.CS104_Connection_sendStopDT(self.connection)

    def set_asdu_received_handler(self, handler):
        """设置ASDU接收回调函数"""
        if not self.connection:
            raise Exception("未建立连接")

        self.asdu_handler = ASDUReceivedHandler(handler)
        lib60870.CS104_Connection_setASDUReceivedHandler(
            self.connection, self.asdu_handler, None
        )

    def send_interrogation(self, cot: CS101_CauseOfTransmission, ca: int, 
                          qoi: QualifierOfInterrogation) -> bool:
        """发送总召唤命令"""
        if not self.connection:
            return False

        return lib60870.CS104_Connection_sendInterrogationCommand(
            self.connection, cot.value, ca, qoi.value
        )

    def send_clock_sync(self, ca: int, dt: datetime.datetime) -> bool:
        """发送时钟同步命令"""
        if not self.connection:
            return False

        cp_time = CP56Time2a.from_datetime(dt)
        return lib60870.CS104_Connection_sendClockSyncCommand(
            self.connection, ca, cp_time
        )

    def send_read_command(self, ca: int, ioa: int) -> bool:
        """发送读命令"""
        if not self.connection:
            return False

        return lib60870.CS104_Connection_sendReadCommand(self.connection, ca, ioa)

    def is_transmit_buffer_full(self) -> bool:
        """检查发送缓冲区是否满"""
        if not self.connection:
            return True
        return lib60870.CS104_Connection_isTransmitBufferFull(self.connection)

    def close(self):
        """关闭连接，确保参数有效"""
        if self.connection is not None:
            # 检查库中是否存在关闭函数
            if hasattr(lib60870, "CS104_Connection_close"):
                try:
                    # 确保connection是有效的c_void_p类型
                    if isinstance(self.connection, ctypes.c_void_p):
                        lib60870.CS104_Connection_close(self.connection)
                    else:
                        # 尝试强制转换为c_void_p（处理意外的类型错误）
                        lib60870.CS104_Connection_close(ctypes.c_void_p(self.connection))
                except Exception as e:
                    print(f"关闭连接时出错: {e}")
            # 标记连接为已关闭
            self.connection = None

    def __del__(self):
        """析构函数，确保连接关闭"""
        self.close()


# 使用示例
if __name__ == "__main__":
    def asdu_received_handler(parameter, asdu):
        """ASDU接收回调示例"""
        print(f"收到ASDU: {asdu}")

    # 创建客户端并连接
    client = CS104Client("127.0.0.1", 2404)
    if client.connect():
        print("连接成功")
        
        # 设置ASDU接收回调
        client.set_asdu_received_handler(asdu_received_handler)
        
        # 开始数据传输
        client.start_data_transmission()
        
        # 发送总召唤
        client.send_interrogation(
            CS101_CauseOfTransmission.REQUEST,
            1,  # 公共地址
            QualifierOfInterrogation.STATION_INTERROGATION
        )
        
        # 发送时钟同步
        client.send_clock_sync(1, datetime.datetime.now())
        
        # 等待一段时间接收数据
        import time
        time.sleep(10)
        
        # 停止数据传输
        client.stop_data_transmission()
        
        # 关闭连接
        client.close()
    else:
        print("连接失败")